/**
 * \file MaCICtrlServer.i
 * \brief Python bindings for following by Tapio Taipalus <tapio.taipalus@tkk.fi>
 * \brief MaCICtrl Server interface header "MaCICtrlServer.hpp".
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 * $Id: MaCICtrlServer.i,v 1.1 2009-05-20 12:26:01 ttaipalu Exp $
 *
 */
%include std_list.i
%include std_string.i
%include std_map.i

%{
#include "MaCICtrlData.hpp"
#include "sync.hpp"
#include "thread.hpp"
#include "ownutils.h"
#include "MaCIError.hpp"
#include "gimutils.h"
#include <list>
#include <map>
#include <string>
#include "MaCICtrlServer.hpp"
%}
// Forward declare gimi::GIMI
namespace gimi {
  class GIMI;
  class GIMIMessage;
}

namespace MaCI {

  namespace MaCICtrl {

    /** Client access data structure.
     *
     * lastaccesstime is initialized to 'current time'.
     */
    struct TClientAccessData {
      TClientAccessData(const MaCI::MaCICtrl::TAccess &aAccess = MaCI::MaCICtrl::TAccess())
        : access(aAccess),
          lastaccesstime(true) {}
      
      MaCI::MaCICtrl::TAccess access;
      gim::time lastaccesstime; ///< Time of last access.
    };


    /** Type for storing TAccess entries for nodes.
     *
     * key = nodeID \n
     * data = TClientAccessData
     */
    typedef std::map<unsigned int, TClientAccessData> TClientAccessMap;


    /** Type for pair consisting of data types in the ClientAccess
     * map.
     */
    typedef std::pair<unsigned int, TClientAccessData> TClientAccessDataPair;
    

    /** Structure for Service authentication key (std::map key)
     */
    struct TServiceAuthenticationKey {
      TServiceAuthenticationKey(const unsigned int aMajor, const unsigned int aMinor)
        : majornum(aMajor),
          minornum(aMinor) {}      
      /** Compare for equality.
       */
      bool operator == (const TServiceAuthenticationKey &aKey) const {
        return ( majornum == aKey.majornum &&
                 minornum == aKey.minornum );
      }
      
      /** Compare for less-than.
       */
      bool operator < (const TServiceAuthenticationKey &aKey) const {
        // This block returns true if current entry is "smaller",
        // otherwise falls to false.
        if (majornum < aKey.majornum) return true;
        else if (majornum == aKey.majornum) {
          if (minornum < aKey.minornum) return true;
        }
        return false;
      }
      
      unsigned int majornum; ///< Major number of key
      unsigned int minornum; ///< Minor number of key
    };
    

    /**
     * Structure for service authentication data.
     */
    struct TServiceAuthenticationData {
      TServiceAuthenticationData(const MaCI::MaCICtrl::TAuthentication &aAuth,
                                 const int aMaxAccessCount = -1,
                                 const int aAccessTimeout_ms = -1)
        : authentication(aAuth),
          maxaccesscount(aMaxAccessCount),
          accesstimeout(aAccessTimeout_ms),
          clientAccessMap() {}

      TClientAccessMap::const_iterator GetClientAccessEntry(const unsigned int aNodeID) const {
        return clientAccessMap.find(aNodeID);
      }
      TClientAccessMap::iterator GetClientAccessEntry(const unsigned int aNodeID) {
        return clientAccessMap.find(aNodeID);
      }
      void SetClientAccessEntry(const unsigned int aNodeID, const MaCI::MaCICtrl::TClientAccessData &aData) {
        TClientAccessMap::iterator i = clientAccessMap.find(aNodeID);
        if (i == clientAccessMap.end()) {
          clientAccessMap.insert(TClientAccessDataPair(aNodeID, aData));
        } else {
          i->second = aData;
        }
      }
      bool RemoveClientAccessEntry(const unsigned int aNodeID) {
        TClientAccessMap::iterator i = clientAccessMap.find(aNodeID);
        if (i != clientAccessMap.end()) {
          clientAccessMap.erase(i);
          dPrint(1,"Removed entry from AccessMap");
          return true;
        }
        return false;
      }
      unsigned int GetAccessCount(void) const {
        return clientAccessMap.size();
      }

    public:
      MaCI::MaCICtrl::TAuthentication authentication; ///< Required authentication credentials
      int maxaccesscount; ///< Maximum number of concurrent accessors.
      int accesstimeout; ///< Timeout of access in milliseconds.
      MaCI::MaCICtrl::TClientAccessMap clientAccessMap; ///< Map of client access entries for this service.
    };


    /** Type for storing TAuthentication entries for services.
     */
    typedef std::map<TServiceAuthenticationKey, TServiceAuthenticationData> TServiceAuthenticationMap;


    /** Type for pair consisting of data types in the
     * ServiceAuthentication map.
     */
    typedef std::pair<TServiceAuthenticationKey, TServiceAuthenticationData> TServiceAuthenticationDataPair;
    
    
    /** MaCI - MaCICtrl Server interface
     * 
     * This class contains the methods and types for providing
     * the MaCICtrl service in the GIMnet.
     */
    class CMaCICtrlServer : private gim::CSync,
                            private gim::CThread
                            
    {
    public:
      /** Constructor.
       * 
       * Constructor of the MaCICtrlServer. This requires;
       * a pointer to Initialized and connected GIMI, minor
       * number of this interface (Used as GIMI minor number)
       *
       * @param[in] aGIMIPtr Pointer to GIMI used for communication
       * @param[in] aInterfaceMinor Interface minor number. If this parameter
       *            is negative, the minor number will be autoprobed.
       */
      CMaCICtrlServer(gimi::GIMI *aGIMIPtr, const int aInterfaceMinor = -1);


      /** Destructor.
       */
      ~CMaCICtrlServer();
 

      /** Open Interface.
       *
       * Basicly registers to GIMI.
       *  
       * @return                'true' when OK, 'false' when not.
       *                        
       */
      MaCI::EMaCIError Open(void);


      /** Close Interface.
       *
       * Closes all GIMI registrations and queues.
       *
       * @return                'true' when OK, 'false' when not.
       *
       */
      MaCI::EMaCIError Close(void);
    

      /** Assign group name.
       *
       * This function assigns a name of the MaCI service group this
       * service should be part of. 
       * 
       * @param[in] aName       Name of the group to bind to.
       * @return                'true' if binding was succesfull (valid name)
       *                        otherwise 'false' on failure.
       */
      bool SetGroupName(const std::string &aName);


      /** Return current group name.
       *
       * @return                Currently set Group name.
       */
      std::string GetGroupName(void) const;

      
      /** Register a new GIMI Major/Minor pair to be part of this MaCI
       * group.
       *
       */
      bool AddGroupService(const unsigned int aMajor, 
                           const unsigned int aMinor,
                           const MaCI::MaCICtrl::EServiceType aServiceType,
                           const std::string &aInterfaceInstanceName = std::string());


      /** Remove a GIMI Major/Minor pair from this MaCI group.
       *
       */
      bool RemoveGroupService(const unsigned int aMajor, 
                              const unsigned int aMinor,
                              const MaCI::MaCICtrl::EServiceType aServiceType);


      /** Clear all GIMI Major/Minor pairs from this MaCI group.
       *
       */
      void ClearGroupServices(void);


      /** Return constant reference to Service Array information.
       *
       */
      const MaCI::MaCICtrl::TServiceArray &GetServiceArray(void) const;


      /** Set the Access key.
       *
       * If this key is unset, everybody can ACCESS. If this key is
       * SET, everybody requesting ACCESS must have a matching key.
       *
       * @param[in] aMajor      Major number to set key for.
       * @param[in] aMinor      Minor number to set key for.
       * @param[in] aKey        Pointer to beginning of the key.
       * @param[in] aKeyLen     Number of bytes belonging to key.
       *                        Atmost 16 bytes of the key are used. However,
       *                        all bytes are meaningful, so the final key
       *                        is padded with zeroes to full length.
       * @param[in] aMaxAccess  Maximum number of simultaneous accesses to this interface.
       *                        Negative value stands for 'unlimited'. 
       *                        Default is 'unlimited'.
       * @param[in] aAccessTimeout_ms Number of milliseconds to keep the 
       *                        access valid. Negative value stands for 'Forever'.
       *                        Default is 60000ms (1 minute). (Keeping this !unlimited
       *                        allows old entries to be removed, instead of
       *                        being stored forever.
       */
      void SetAccessKey(const unsigned int aMajor, 
                        const unsigned int aMinor,
                        const unsigned char *aKey, 
                        const unsigned int aKeyLen,
                        const int aMaxAccess = -1,
                        const int aAccessTimeout_ms = 60000);
      
      /** \overload 
       */
      void SetAccessKey(const unsigned int aMajor, 
                        const unsigned int aMinor,
                        const MaCI::MaCICtrl::TAuthentication &aAuthentication,
                        const int aMaxAccess = -1,
                        const int aAccessTimeout_ms = 60000) {
        SetAccessKey(aMajor, aMinor, 
                     aAuthentication.keyAccess, sizeof(aAuthentication.keyAccess), 
                     aMaxAccess, aAccessTimeout_ms);
      }


      
      /** Get TAccess information for the specified client.
       *
       * @param[out] aAccess    TAccess information is stored to this reference.
       * @param[in] aMajor      Major number to get access for.
       * @param[in] aMinor      Minor number to get access for.
       * @param[in] aNodeID     Client ID to get access for.
       * @param[in] aUpdateAccessTime Update the node access time? If
       *                        this is set, this function call
       *                        updates the last update time of the
       *                        specified node (if all else is OK)
       */
      void GetAccessInfo(MaCI::MaCICtrl::TAccess &aAccess, 
                         const unsigned int aMajor, 
                         const unsigned int aMinor,
                         const unsigned int aNodeID,
                         const bool aUpdateAccessTime);


      /** Set Client authentication information.
       *
       */
      bool SetClientAuthentication(const unsigned int aMajor,
                                   const unsigned int aMinor,
                                   const unsigned int aNodeID,
                                   const MaCI::MaCICtrl::TAuthentication &aAuth);
        
      /**
       * Returns a pointer to GIMI backend used by this MaCICtrl
       * instance.
       * 
       * @return                Mutable pointer to GIMI instance used as backend.
       */
      gimi::GIMI *GetGIMIPtr(void) { return iGIMIPtr; }


      /** Return whether the MaCICtrlServer is active or not.
       *
       * @return                'true' if this MaCICtrlServer session is
       *                        Active. 'false' if this session is not Active.
       *
       */
      inline bool IsActive(void) const { return iServerActive; }
      
/*    private:
      CMaCICtrlServer(const CMaCICtrlServer &)
        : CSync(),
          CThread(),
          iServerActive(),
          iInformationMessage(),
          iInformationMessageDirty(),
          iMaCIGroupName(),
          iMaCIGroupID(),
          iServiceArray(),
          iServiceIdentificationMap(),
          iServiceAuthentication(),
          iGIMIPtr(),
          iInterfaceMajor(), 
          iInterfaceMinor(),
          iRedirectionBufferId() { abort(); }
      CMaCICtrlServer &operator=(const CMaCICtrlServer &) { abort(); return *this; }

      // Priv. func.
      int ThreadFunction(const int aThreadNumber); ///< Incoming data handler function
      int ProcessMessage(const gimi::GIMIMessage &msg); ///< Incoming data processor function

      // App. Specific functions
      void CommandGetAccess(CMaCICtrlData &aReply,
                            const CMaCICtrlData &aInputData,
                            const gimi::GIMIMessage &aMsg);
      void CommandGetInformation(CMaCICtrlData &aReply,
                                 const CMaCICtrlData &aInputData,
                                 const gimi::GIMIMessage &aMsg);
      void CommandSetAuthentication(const CMaCICtrlData &aInputData, 
                                    const gimi::GIMIMessage &aMsg);
      TServiceArrayIterator FindService(const unsigned int aMajor, 
                                        const unsigned int aMinor,
                                        const EServiceType aType);

      // App. specific variables
      bool iServerActive; ///< Is the server active? - flag

      CMaCICtrlData iInformationMessage; ///< Container for information reply
      bool iInformationMessageDirty;

      std::string iMaCIGroupName; ///< Group name
      unsigned int iMaCIGroupID; ///< Group ID
      TServiceArray iServiceArray; ///< Array of services part of this MaCI Group
      TServiceIdentificationMap iServiceIdentificationMap; ///< Identification information for services.

      // Authentication infos
      bool AuthenticateClient(const unsigned int aNodeID, const TAuthentication &aAuthentication);
      bool UpdateAccess(TClientAccessMap::iterator &accessIterator,
                        TServiceAuthenticationData &aService);
  
      void CheckServiceAccessTimeouts(TServiceAuthenticationData &aService);
      TServiceAuthenticationMap iServiceAuthentication; ///< Service authentication.

      // Datafields normally contained in MaCI
      gimi::GIMI *iGIMIPtr;   ///< Common pointer to GIMI (may be shared instance)
      int iInterfaceMajor;    ///< Interface Major number (GIMI Major number)
      int iInterfaceMinor;    ///< Interface Minor number (GIMI Minor number)
      unsigned int iRedirectionBufferId; ///< GIMI Redirection buffer identifier
*/

    };

  }
}


